About
Spectral Teletype is an interactive audio-visual piece that turns messages into melodies. Participants are invited to transmit messages from their smartphones, and the messages are used to simultaneously display and play a synthetic representation of the text in a scrolling audio spectrum.
Melodies are created from strings of characters by printing the characters into the audio spectrum. In order to sonically print the characters, an oscillator bank is configured as an aural dot matrix printer, and the characters are divided into 32 by 32 pixel grids. The oscillator bank plays these pixels as sine waves in harmonic relationships, creating notes that represent the shapes of the characters in the audio spectrum.
The pitch of each character is selected from a major eleventh chord, with more frequently used characters the toward the root of the chord. Additionally, the chord is modulated between scale degrees I, IV, and V based on the first character of each word.
This work was created with custom software written in the Pure Data, HTML, PHP, and C programming languages.
Spectral Teletype was exhibited in 2015 during the Make Music San Diego event at the San Diego Art Institute, and in the 2015 Ephemeral Objects show at the San Diego Art Institute.
Video
Photos
Code
A couple of custom Pure Data objects were used to realize this piece. One is an oscillator bank, named teletype~, that prints the characters as sound. The other object is a fifo buffer for ascii messages, named asciibuf, that allows strings of characters to be stored en masse while individual characters are printed at regular intervals. The C code for these objects is shown below.
teletype~.c
//------------------------------------------------------------------------------
// Spectral ASCII Printer
//
// teletype~.c
//
// Spectrally Prints ASCII Characters Using Harmonically Related Oscillators
//
// Created by Cooper Baker on 05/10/15.
// Copyright (c) 2015 Cooper Baker. All rights reserved.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// headers
//------------------------------------------------------------------------------
// main header for pd
#include "m_pd.h"
// teletype~ font file
#include "teletype~.font"
// utility header for Pd Objects project
#include "utility.h"
// disable compiler warnings on windows
#ifdef NT
#pragma warning( disable : 4244 )
#pragma warning( disable : 4305 )
#endif
// 2^13 point wavetable
#define WAVETABLE_SIZE 8192
#define WAVETABLE_MASK 8191
// number of partials
#define OSCILLATORS 32
//------------------------------------------------------------------------------
// teletype_class - pointer to this object's definition
//------------------------------------------------------------------------------
static t_class* teletype_class;
//------------------------------------------------------------------------------
// teletype - data structure holding this object's data
//------------------------------------------------------------------------------
typedef struct teletype
{
// this object - must always be first variable in struct
t_object object;
// needed for CLASS_MAINSIGNALIN macro call in teletype_tilde_setup
t_float inlet_1;
// fundamental frequency
t_float freq;
// frequency smoothing variable
t_float freq_smooth;
// pointer to the wavetable
t_float* wavetable;
// amplitude of each oscillator
t_float* amp;
// smoothed amplitude of each oscillator
t_float* amp_smooth;
// amplitude smoothing increments
t_float* amp_smooth_inc;
// oscbank phase
t_float* phase;
// oscbank phase increment
t_float* phase_inc;
// waveform synthesis buffers
t_float* raw;
t_float* saw;
// size of signal vector
t_float vector_bytes;
// sample rate of object
t_float sample_rate;
// milliseconds per pixel
t_float pixel_msec;
// font bitmap of characters
t_float* fontmap;
// counter for samples per pixel
t_float pixel_samps;
// x pixel location within character
t_int x_pixel;
// character offset within fontmap
t_int char_offset;
// print character flag
t_int print_char;
} t_teletype;
//------------------------------------------------------------------------------
// function prototypes
//------------------------------------------------------------------------------
static void teletype_float ( t_teletype* object, t_floatarg number );
static void teletype_message ( t_teletype* object, t_symbol* selector, t_int items, t_atom* list );
static t_int* teletype_perform ( t_int* io );
static void teletype_dsp ( t_teletype* object, t_signal **sig );
static void* teletype_new ( t_symbol *s, t_int argc, t_atom *argv );
static void teletype_free ( t_teletype* object );
void teletype_tilde_setup ( void );
//------------------------------------------------------------------------------
// teletype_float - handles float input
//------------------------------------------------------------------------------
static void teletype_float( t_teletype* object, t_floatarg number )
{
// ignore out of range numbers ( range: ascii 32 to 94 )
if( ( number < 32 ) || ( number > 126 ) )
{
return;
}
// translate ascii to local
number -= 32;
// reset character position
object->pixel_samps = 0;
object->x_pixel = 0;
// calculate character offset
object->char_offset = number * fontmap_x_px * fontmap_x_px;
// set print character flag
object->print_char = TRUE;
}
//------------------------------------------------------------------------------
// teletype_message - handles input messages
//------------------------------------------------------------------------------
static void teletype_message( t_teletype* object, t_symbol* selector, t_int items, t_atom* list )
{
char* message = selector->s_name;
if( StringMatch( message, "msec" ) )
{
if( items < 1 )
{
pd_error( object, "teletype~: no msec argument" );
return;
}
if( list[ 0 ].a_type == A_FLOAT )
{
t_float msec = list[ 0 ].a_w.w_float;
ClipMin( msec, 1 );
object->pixel_msec = msec / fontmap_x_px;
}
else
{
pd_error( object, "teletype~: invalid msec argument type" );
}
if( items > 1 )
{
pd_error( object, "teletype~: extra msec arguments ignored" );
}
}
if( StringMatch( message, "freq" ) )
{
if( items < 1 )
{
pd_error( object, "teletype~: no freq argument" );
return;
}
if( list[ 0 ].a_type == A_FLOAT )
{
t_float freq = list[ 0 ].a_w.w_float;
ClipMin( freq, 1 );
object->freq = freq;
}
else
{
pd_error( object, "teletype~: invalid freq argument type" );
}
if( items > 1 )
{
pd_error( object, "teletype~: extra freq arguments ignored" );
}
}
}
//------------------------------------------------------------------------------
// teletype_perform - the signal processing function of this object
//------------------------------------------------------------------------------
static t_int* teletype_perform( t_int* io )
{
// store variables from dsp input/output array
t_float* in1 = ( t_float* )( io[ 1 ] );
t_float* out1 = ( t_float* )( io[ 2 ] );
t_float* out2 = ( t_float* )( io[ 3 ] );
t_int frames = ( t_int )( io[ 4 ] );
t_teletype* object = ( t_teletype* )( io[ 5 ] );
// store pointers locally
t_float* wavetable = object->wavetable;
t_float* raw = object->raw;
t_float* saw = object->saw;
t_float* amp = object->amp;
t_float* amp_smooth = object->amp_smooth;
t_float* amp_smooth_inc = object->amp_smooth_inc;
t_float* phase = object->phase;
t_float* phase_inc = object->phase_inc;
t_float* fontmap = object->fontmap;
t_float smooth_coeff = 1.0 / frames;
// calculate samples per pixel based
t_float samps_per_pixel = object->pixel_msec * 0.001 * object->sample_rate;
// wavetable & mask value
long wavetable_mask = WAVETABLE_MASK;
// calculate fundamental phase increment
t_float fund_phase_inc = object->freq * ( 1.0 / object->sample_rate ) * WAVETABLE_SIZE;
// iterator variables
t_int n = -1;
t_int o = -1;
// clear output array
memset( raw, 0, object->vector_bytes );
memset( saw, 0, object->vector_bytes );
// oscillator bank
//--------------------------------------------------------------------------
// if a character is ready to print
if( object->print_char )
{
// iterate through the oscillators
while( ( ++o < OSCILLATORS ) && ( object->print_char == TRUE ) )
{
// set target amplitude based on fontmap pixel values
amp[ o ] = fontmap[ o * (int)fontmap_x_px + object->char_offset + object->x_pixel ];
// calculate amplitude smoothing increments
amp_smooth_inc[ o ] = ( amp[ o ] - amp_smooth[ o ] ) * smooth_coeff;
// calculate phase increment for each partial
phase_inc[ o ] = fund_phase_inc * ( OSCILLATORS - o );
// reset vector iterator
n = -1;
// iterate through the signal vector
while( ++n < frames )
{
// calculate fontmap variables
if( o == 0 )
{
// increment samples per pixel count
object->pixel_samps++;
// if pixel samples have elapsed
if( object->pixel_samps >= samps_per_pixel )
{
// reset samples per pixel count
object->pixel_samps = 0;
// increment pixel count
object->x_pixel++;
// if pixel count is done
if( object->x_pixel >= fontmap_x_px )
{
// reset pixel count
object->x_pixel = 0;
// stop printing the character
object->print_char = FALSE;
// end synthesis
break;
}
}
}
// accumulate waveforms into waveform arrays
raw[ n ] += wavetable[ ( unsigned long )phase[ o ] ] * amp_smooth[ o ];
saw[ n ] += wavetable[ ( unsigned long )phase[ o ] ] * amp_smooth[ o ] * ( 1.0 / ( ( float )( OSCILLATORS - o ) / 3.0 ) );
// increment phases
phase[ o ] += phase_inc[ o ];
// & wrap phases while preserving decimal values
phase[ o ] = ( ( long ) phase[ o ] & wavetable_mask ) + ( phase[ o ] - ( long )phase[ o ] );
// increment amplitudes
amp_smooth[ o ] += amp_smooth_inc[ o ];
}
// set smooth amplitude to actual amplitude
amp_smooth[ o ] = amp[ o ];
}
}
// copy waveforms to output buffers
memcpy( out1, raw, object->vector_bytes );
memcpy( out2, saw, object->vector_bytes );
// return the dsp input/output array address plus one more than its size
// to provide a pointer to the next perform function in pd's call list
return &( io[ 6 ] );
}
//------------------------------------------------------------------------------
// teletype_dsp - installs this object's dsp function in pd's callback list
//------------------------------------------------------------------------------
static void teletype_dsp( t_teletype* object, t_signal **sig )
{
// store sample rate
object->sample_rate = sig[ 0 ]->s_sr;
// calculate size of vector
object->vector_bytes = sig[ 0 ]->s_n * sizeof( t_float );
// reallocate memory
object->raw = ( t_float* )realloc( object->raw, object->vector_bytes );
object->saw = ( t_float* )realloc( object->saw, object->vector_bytes );
object->amp = ( t_float* )realloc( object->amp, object->vector_bytes );
object->amp_smooth = ( t_float* )realloc( object->amp_smooth, object->vector_bytes );
object->amp_smooth_inc = ( t_float* )realloc( object->amp_smooth_inc, object->vector_bytes );
// clear memory
memset( object->raw, 0, object->vector_bytes );
memset( object->saw, 0, object->vector_bytes );
memset( object->amp, 0, object->vector_bytes );
memset( object->amp_smooth, 0, object->vector_bytes );
memset( object->amp_smooth_inc, 0, object->vector_bytes );
// dsp_add arguments
//--------------------------------------------------------------------------
// perform routine
// number of passed parameters
// inlet 1 sample vector
// inlet 2 sample vector
// outlet sample vector
// sample frames to process (vector size)
dsp_add( teletype_perform, 5, sig[ 0 ]->s_vec, sig[ 1 ]->s_vec, sig[ 2 ]->s_vec, sig[ 0 ]->s_n, object );
}
//------------------------------------------------------------------------------
// teletype_new - instantiates a copy of this object in pd
//------------------------------------------------------------------------------
static void* teletype_new( t_symbol *s, t_int argc, t_atom *argv )
{
// create a pointer to this object
t_teletype* object = ( t_teletype* )pd_new( teletype_class );
// create signal outlets for this object
outlet_new( &object->object, gensym( "signal" ) );
outlet_new( &object->object, gensym( "signal" ) );
// initialize the value of inlet_1 variable
object->inlet_1 = 0;
// initialize variables
object->freq = 300;
object->phase = 0;
object->phase_inc = 0;
object->vector_bytes = 0;
object->sample_rate = 0;
// initialize pointers
object->wavetable = NULL;
object->raw = NULL;
object->saw = NULL;
object->amp = NULL;
object->amp_smooth = NULL;
object->amp_smooth_inc = NULL;
object->phase = NULL;
object->phase_inc = NULL;
object->fontmap = NULL;
// allocate memory
object->wavetable = ( t_float* )calloc( WAVETABLE_SIZE, sizeof( t_float ) );
object->phase = ( t_float* )calloc( OSCILLATORS, sizeof( t_float ) );
object->phase_inc = ( t_float* )calloc( OSCILLATORS, sizeof( t_float ) );
object->fontmap = ( t_float* )calloc( fontmap_size, sizeof( t_float ) );
// fill the fontmap
memcpy( object->fontmap, fontmap_init, fontmap_size * sizeof( t_float ) );
// temporary wavetable calculation variables
long i = -1;
t_float x;
// fill the wavetable with a single sine wave cycle
while( ++i < WAVETABLE_SIZE )
{
x = ( float )i / ( float )WAVETABLE_SIZE;
object->wavetable[ i ] = Sine( C_2_PI * x );
}
return object;
}
//------------------------------------------------------------------------------
// teletype_free - cleans up memory allocated by this object
//------------------------------------------------------------------------------
static void teletype_free( t_teletype* object )
{
// if memory is allocated
if( object->wavetable )
{
// deallocate the memory
free( object->wavetable );
// set the memory pointer to null
object->wavetable = NULL;
}
// . . .
if( object->raw )
{
free( object->raw );
object->raw = NULL;
}
if( object->saw )
{
free( object->saw );
object->saw = NULL;
}
if( object->amp )
{
free( object->amp );
object->amp = NULL;
}
if( object->amp_smooth )
{
free( object->amp_smooth );
object->amp_smooth = NULL;
}
if( object->amp_smooth_inc )
{
free( object->amp_smooth_inc );
object->amp_smooth_inc = NULL;
}
if( object->phase )
{
free( object->phase );
object->phase = NULL;
}
if( object->phase_inc )
{
free( object->phase_inc );
object->phase_inc = NULL;
}
if( object->fontmap )
{
free( object->fontmap );
object->fontmap = NULL;
}
}
//------------------------------------------------------------------------------
// teletype_tilde_setup - describes the attributes of this object to pd so it may be properly instantiated
// (must always be named with _tilde replacing ~ in the object name)
//------------------------------------------------------------------------------
void teletype_tilde_setup( void )
{
// teletype class
//--------------------------------------------------------------------------
// creates an instance of this object and describes it to pd
teletype_class = class_new( gensym( "teletype~" ), ( t_newmethod )teletype_new, ( t_method )teletype_free, sizeof( t_teletype ), 0, A_GIMME, 0 );
// declares leftmost inlet as a signal inlet
CLASS_MAINSIGNALIN( teletype_class, t_teletype, inlet_1 );
// installs teletype_dsp so that it will be called when dsp is turned on
class_addmethod( teletype_class, ( t_method )teletype_dsp, gensym( "dsp" ), 0 );
// add float handler
class_addfloat( teletype_class, teletype_float );
// add message handler that responds to any message
class_addmethod( teletype_class, ( t_method )teletype_message, gensym( "anything" ), A_GIMME, 0 );
// announce this object in the pd console
Announce( "teletype~: spectral ascii printer - v1.0 - Cooper Baker" );
}
//------------------------------------------------------------------------------
// EOF
//------------------------------------------------------------------------------
// Spectral ASCII Printer
//
// teletype~.c
//
// Spectrally Prints ASCII Characters Using Harmonically Related Oscillators
//
// Created by Cooper Baker on 05/10/15.
// Copyright (c) 2015 Cooper Baker. All rights reserved.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// headers
//------------------------------------------------------------------------------
// main header for pd
#include "m_pd.h"
// teletype~ font file
#include "teletype~.font"
// utility header for Pd Objects project
#include "utility.h"
// disable compiler warnings on windows
#ifdef NT
#pragma warning( disable : 4244 )
#pragma warning( disable : 4305 )
#endif
// 2^13 point wavetable
#define WAVETABLE_SIZE 8192
#define WAVETABLE_MASK 8191
// number of partials
#define OSCILLATORS 32
//------------------------------------------------------------------------------
// teletype_class - pointer to this object's definition
//------------------------------------------------------------------------------
static t_class* teletype_class;
//------------------------------------------------------------------------------
// teletype - data structure holding this object's data
//------------------------------------------------------------------------------
typedef struct teletype
{
// this object - must always be first variable in struct
t_object object;
// needed for CLASS_MAINSIGNALIN macro call in teletype_tilde_setup
t_float inlet_1;
// fundamental frequency
t_float freq;
// frequency smoothing variable
t_float freq_smooth;
// pointer to the wavetable
t_float* wavetable;
// amplitude of each oscillator
t_float* amp;
// smoothed amplitude of each oscillator
t_float* amp_smooth;
// amplitude smoothing increments
t_float* amp_smooth_inc;
// oscbank phase
t_float* phase;
// oscbank phase increment
t_float* phase_inc;
// waveform synthesis buffers
t_float* raw;
t_float* saw;
// size of signal vector
t_float vector_bytes;
// sample rate of object
t_float sample_rate;
// milliseconds per pixel
t_float pixel_msec;
// font bitmap of characters
t_float* fontmap;
// counter for samples per pixel
t_float pixel_samps;
// x pixel location within character
t_int x_pixel;
// character offset within fontmap
t_int char_offset;
// print character flag
t_int print_char;
} t_teletype;
//------------------------------------------------------------------------------
// function prototypes
//------------------------------------------------------------------------------
static void teletype_float ( t_teletype* object, t_floatarg number );
static void teletype_message ( t_teletype* object, t_symbol* selector, t_int items, t_atom* list );
static t_int* teletype_perform ( t_int* io );
static void teletype_dsp ( t_teletype* object, t_signal **sig );
static void* teletype_new ( t_symbol *s, t_int argc, t_atom *argv );
static void teletype_free ( t_teletype* object );
void teletype_tilde_setup ( void );
//------------------------------------------------------------------------------
// teletype_float - handles float input
//------------------------------------------------------------------------------
static void teletype_float( t_teletype* object, t_floatarg number )
{
// ignore out of range numbers ( range: ascii 32 to 94 )
if( ( number < 32 ) || ( number > 126 ) )
{
return;
}
// translate ascii to local
number -= 32;
// reset character position
object->pixel_samps = 0;
object->x_pixel = 0;
// calculate character offset
object->char_offset = number * fontmap_x_px * fontmap_x_px;
// set print character flag
object->print_char = TRUE;
}
//------------------------------------------------------------------------------
// teletype_message - handles input messages
//------------------------------------------------------------------------------
static void teletype_message( t_teletype* object, t_symbol* selector, t_int items, t_atom* list )
{
char* message = selector->s_name;
if( StringMatch( message, "msec" ) )
{
if( items < 1 )
{
pd_error( object, "teletype~: no msec argument" );
return;
}
if( list[ 0 ].a_type == A_FLOAT )
{
t_float msec = list[ 0 ].a_w.w_float;
ClipMin( msec, 1 );
object->pixel_msec = msec / fontmap_x_px;
}
else
{
pd_error( object, "teletype~: invalid msec argument type" );
}
if( items > 1 )
{
pd_error( object, "teletype~: extra msec arguments ignored" );
}
}
if( StringMatch( message, "freq" ) )
{
if( items < 1 )
{
pd_error( object, "teletype~: no freq argument" );
return;
}
if( list[ 0 ].a_type == A_FLOAT )
{
t_float freq = list[ 0 ].a_w.w_float;
ClipMin( freq, 1 );
object->freq = freq;
}
else
{
pd_error( object, "teletype~: invalid freq argument type" );
}
if( items > 1 )
{
pd_error( object, "teletype~: extra freq arguments ignored" );
}
}
}
//------------------------------------------------------------------------------
// teletype_perform - the signal processing function of this object
//------------------------------------------------------------------------------
static t_int* teletype_perform( t_int* io )
{
// store variables from dsp input/output array
t_float* in1 = ( t_float* )( io[ 1 ] );
t_float* out1 = ( t_float* )( io[ 2 ] );
t_float* out2 = ( t_float* )( io[ 3 ] );
t_int frames = ( t_int )( io[ 4 ] );
t_teletype* object = ( t_teletype* )( io[ 5 ] );
// store pointers locally
t_float* wavetable = object->wavetable;
t_float* raw = object->raw;
t_float* saw = object->saw;
t_float* amp = object->amp;
t_float* amp_smooth = object->amp_smooth;
t_float* amp_smooth_inc = object->amp_smooth_inc;
t_float* phase = object->phase;
t_float* phase_inc = object->phase_inc;
t_float* fontmap = object->fontmap;
t_float smooth_coeff = 1.0 / frames;
// calculate samples per pixel based
t_float samps_per_pixel = object->pixel_msec * 0.001 * object->sample_rate;
// wavetable & mask value
long wavetable_mask = WAVETABLE_MASK;
// calculate fundamental phase increment
t_float fund_phase_inc = object->freq * ( 1.0 / object->sample_rate ) * WAVETABLE_SIZE;
// iterator variables
t_int n = -1;
t_int o = -1;
// clear output array
memset( raw, 0, object->vector_bytes );
memset( saw, 0, object->vector_bytes );
// oscillator bank
//--------------------------------------------------------------------------
// if a character is ready to print
if( object->print_char )
{
// iterate through the oscillators
while( ( ++o < OSCILLATORS ) && ( object->print_char == TRUE ) )
{
// set target amplitude based on fontmap pixel values
amp[ o ] = fontmap[ o * (int)fontmap_x_px + object->char_offset + object->x_pixel ];
// calculate amplitude smoothing increments
amp_smooth_inc[ o ] = ( amp[ o ] - amp_smooth[ o ] ) * smooth_coeff;
// calculate phase increment for each partial
phase_inc[ o ] = fund_phase_inc * ( OSCILLATORS - o );
// reset vector iterator
n = -1;
// iterate through the signal vector
while( ++n < frames )
{
// calculate fontmap variables
if( o == 0 )
{
// increment samples per pixel count
object->pixel_samps++;
// if pixel samples have elapsed
if( object->pixel_samps >= samps_per_pixel )
{
// reset samples per pixel count
object->pixel_samps = 0;
// increment pixel count
object->x_pixel++;
// if pixel count is done
if( object->x_pixel >= fontmap_x_px )
{
// reset pixel count
object->x_pixel = 0;
// stop printing the character
object->print_char = FALSE;
// end synthesis
break;
}
}
}
// accumulate waveforms into waveform arrays
raw[ n ] += wavetable[ ( unsigned long )phase[ o ] ] * amp_smooth[ o ];
saw[ n ] += wavetable[ ( unsigned long )phase[ o ] ] * amp_smooth[ o ] * ( 1.0 / ( ( float )( OSCILLATORS - o ) / 3.0 ) );
// increment phases
phase[ o ] += phase_inc[ o ];
// & wrap phases while preserving decimal values
phase[ o ] = ( ( long ) phase[ o ] & wavetable_mask ) + ( phase[ o ] - ( long )phase[ o ] );
// increment amplitudes
amp_smooth[ o ] += amp_smooth_inc[ o ];
}
// set smooth amplitude to actual amplitude
amp_smooth[ o ] = amp[ o ];
}
}
// copy waveforms to output buffers
memcpy( out1, raw, object->vector_bytes );
memcpy( out2, saw, object->vector_bytes );
// return the dsp input/output array address plus one more than its size
// to provide a pointer to the next perform function in pd's call list
return &( io[ 6 ] );
}
//------------------------------------------------------------------------------
// teletype_dsp - installs this object's dsp function in pd's callback list
//------------------------------------------------------------------------------
static void teletype_dsp( t_teletype* object, t_signal **sig )
{
// store sample rate
object->sample_rate = sig[ 0 ]->s_sr;
// calculate size of vector
object->vector_bytes = sig[ 0 ]->s_n * sizeof( t_float );
// reallocate memory
object->raw = ( t_float* )realloc( object->raw, object->vector_bytes );
object->saw = ( t_float* )realloc( object->saw, object->vector_bytes );
object->amp = ( t_float* )realloc( object->amp, object->vector_bytes );
object->amp_smooth = ( t_float* )realloc( object->amp_smooth, object->vector_bytes );
object->amp_smooth_inc = ( t_float* )realloc( object->amp_smooth_inc, object->vector_bytes );
// clear memory
memset( object->raw, 0, object->vector_bytes );
memset( object->saw, 0, object->vector_bytes );
memset( object->amp, 0, object->vector_bytes );
memset( object->amp_smooth, 0, object->vector_bytes );
memset( object->amp_smooth_inc, 0, object->vector_bytes );
// dsp_add arguments
//--------------------------------------------------------------------------
// perform routine
// number of passed parameters
// inlet 1 sample vector
// inlet 2 sample vector
// outlet sample vector
// sample frames to process (vector size)
dsp_add( teletype_perform, 5, sig[ 0 ]->s_vec, sig[ 1 ]->s_vec, sig[ 2 ]->s_vec, sig[ 0 ]->s_n, object );
}
//------------------------------------------------------------------------------
// teletype_new - instantiates a copy of this object in pd
//------------------------------------------------------------------------------
static void* teletype_new( t_symbol *s, t_int argc, t_atom *argv )
{
// create a pointer to this object
t_teletype* object = ( t_teletype* )pd_new( teletype_class );
// create signal outlets for this object
outlet_new( &object->object, gensym( "signal" ) );
outlet_new( &object->object, gensym( "signal" ) );
// initialize the value of inlet_1 variable
object->inlet_1 = 0;
// initialize variables
object->freq = 300;
object->phase = 0;
object->phase_inc = 0;
object->vector_bytes = 0;
object->sample_rate = 0;
// initialize pointers
object->wavetable = NULL;
object->raw = NULL;
object->saw = NULL;
object->amp = NULL;
object->amp_smooth = NULL;
object->amp_smooth_inc = NULL;
object->phase = NULL;
object->phase_inc = NULL;
object->fontmap = NULL;
// allocate memory
object->wavetable = ( t_float* )calloc( WAVETABLE_SIZE, sizeof( t_float ) );
object->phase = ( t_float* )calloc( OSCILLATORS, sizeof( t_float ) );
object->phase_inc = ( t_float* )calloc( OSCILLATORS, sizeof( t_float ) );
object->fontmap = ( t_float* )calloc( fontmap_size, sizeof( t_float ) );
// fill the fontmap
memcpy( object->fontmap, fontmap_init, fontmap_size * sizeof( t_float ) );
// temporary wavetable calculation variables
long i = -1;
t_float x;
// fill the wavetable with a single sine wave cycle
while( ++i < WAVETABLE_SIZE )
{
x = ( float )i / ( float )WAVETABLE_SIZE;
object->wavetable[ i ] = Sine( C_2_PI * x );
}
return object;
}
//------------------------------------------------------------------------------
// teletype_free - cleans up memory allocated by this object
//------------------------------------------------------------------------------
static void teletype_free( t_teletype* object )
{
// if memory is allocated
if( object->wavetable )
{
// deallocate the memory
free( object->wavetable );
// set the memory pointer to null
object->wavetable = NULL;
}
// . . .
if( object->raw )
{
free( object->raw );
object->raw = NULL;
}
if( object->saw )
{
free( object->saw );
object->saw = NULL;
}
if( object->amp )
{
free( object->amp );
object->amp = NULL;
}
if( object->amp_smooth )
{
free( object->amp_smooth );
object->amp_smooth = NULL;
}
if( object->amp_smooth_inc )
{
free( object->amp_smooth_inc );
object->amp_smooth_inc = NULL;
}
if( object->phase )
{
free( object->phase );
object->phase = NULL;
}
if( object->phase_inc )
{
free( object->phase_inc );
object->phase_inc = NULL;
}
if( object->fontmap )
{
free( object->fontmap );
object->fontmap = NULL;
}
}
//------------------------------------------------------------------------------
// teletype_tilde_setup - describes the attributes of this object to pd so it may be properly instantiated
// (must always be named with _tilde replacing ~ in the object name)
//------------------------------------------------------------------------------
void teletype_tilde_setup( void )
{
// teletype class
//--------------------------------------------------------------------------
// creates an instance of this object and describes it to pd
teletype_class = class_new( gensym( "teletype~" ), ( t_newmethod )teletype_new, ( t_method )teletype_free, sizeof( t_teletype ), 0, A_GIMME, 0 );
// declares leftmost inlet as a signal inlet
CLASS_MAINSIGNALIN( teletype_class, t_teletype, inlet_1 );
// installs teletype_dsp so that it will be called when dsp is turned on
class_addmethod( teletype_class, ( t_method )teletype_dsp, gensym( "dsp" ), 0 );
// add float handler
class_addfloat( teletype_class, teletype_float );
// add message handler that responds to any message
class_addmethod( teletype_class, ( t_method )teletype_message, gensym( "anything" ), A_GIMME, 0 );
// announce this object in the pd console
Announce( "teletype~: spectral ascii printer - v1.0 - Cooper Baker" );
}
//------------------------------------------------------------------------------
// EOF
//------------------------------------------------------------------------------
asciibuf.c
//------------------------------------------------------------------------------
// Pd Spectral Toolkit
//
// asciibuf.c
//
// Accepts mixed float / symbol input and sorts it to corresponding outlets
//
// Created by Cooper Baker on 3/29/12.
// Copyright (c) 2012 Cooper Baker. All rights reserved.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// m_pd.h - main header for Pd
//------------------------------------------------------------------------------
#include "m_pd.h"
// utility header for Pd Spectral Toolkit project
#include "utility.h"
//------------------------------------------------------------------------------
// asciibuf_class - pointer to this object's definition
//------------------------------------------------------------------------------
t_class* asciibuf_class;
//------------------------------------------------------------------------------
// asciibuf - data structure holding this object's data
//------------------------------------------------------------------------------
typedef struct asciibuf
{
// this object - must always be first variable in struct
t_object object;
// pointers to the outlets
t_outlet* outlet;
// pointer to float list
t_float* ascii;
// character count
unsigned long chars;
} t_asciibuf;
//------------------------------------------------------------------------------
// function prototypes
//------------------------------------------------------------------------------
void asciibuf_message ( t_asciibuf* object, t_symbol* selector, t_int items, t_atom* list );
void asciibuf_bang ( t_asciibuf* object );
void* asciibuf_new ( void );
void asciibuf_free ( t_asciibuf* object );
void asciibuf_setup ( void );
//------------------------------------------------------------------------------
// asciibuf_bang - shift out a float ( fifo )
//------------------------------------------------------------------------------
void asciibuf_bang( t_asciibuf* object )
{
if( object->chars )
{
// output first ascii value in list
outlet_float( object->outlet, object->ascii[ 0 ] );
// decrement character count
object->chars--;
// shift the ascii buffer
memmove( object->ascii, &( object->ascii[ 1 ] ), object->chars * sizeof( t_float ) );
// trim the ascii buffer
object->ascii = ( t_float* )realloc( object->ascii, object->chars * sizeof( t_float ) );
}
}
//------------------------------------------------------------------------------
// asciibuf_parse - parses list input
//------------------------------------------------------------------------------
void asciibuf_message( t_asciibuf* object, t_symbol* selector, t_int items, t_atom* list )
{
// iterator for input list
t_int i = 0;
// local ascii character count
unsigned long a = 0;
// check for a bad selector
if ( ( StringMatch( selector->s_name, "float" ) == TRUE ) && ( items == 1 ) )
{
; // do nothing for single floats
}
else if( StringMatch( selector->s_name, "list" ) == FALSE )
{
// display an error
pd_error( object, "asciibuf: unknown selector" );
}
// iterate through the input messages
for( i = 0 ; i < items ; i++ )
{
// if the input message is a float
if( list[ i ].a_type == A_FLOAT )
{
// increment the ascii count
a++;
}
// if the input message is a symbol
else if( list[ i ].a_type == A_SYMBOL )
{
// display an error for bad input
pd_error( object, "asciibuf: non-float list items ignored" );
}
}
// allocate more memory for the incoming values
object->ascii = ( t_float* )realloc( object->ascii, ( object->chars + a ) * sizeof( t_float ) );
// reset local ascii character count
a = 0;
// iterate through the input messages
for( i = 0 ; i < items ; i++ )
{
// if the input message is a float
if( list[ i ].a_type == A_FLOAT )
{
// save the float into ascii list
object->ascii[ object->chars + a ] = list[ i ].a_w.w_float;
// increment the ascii count
a++;
}
}
// update character count
object->chars += a;
}
//------------------------------------------------------------------------------
// asciibuf_new - initialize the object upon instantiation ( aka "constructor" )
//------------------------------------------------------------------------------
void* asciibuf_new( void )
{
// declare a pointer to this class
t_asciibuf* object;
// generate a new object and save its pointer in "object"
object = ( t_asciibuf* )pd_new( asciibuf_class );
// generate two new outlets and save their pointers in the object's struct
object->outlet = outlet_new( &object->object, gensym( "float" ) );
// initialize pointer
object->ascii = NULL;
// initialize character count
object->chars = 0;
// return the pointer to this class
return ( void* )object;
}
//------------------------------------------------------------------------------
// teletype_free - cleans up memory allocated by this object
//------------------------------------------------------------------------------
void asciibuf_free( t_asciibuf* object )
{
// clean up the float list
if( object->ascii )
{
free( object->ascii );
object->ascii = NULL;
}
}
//------------------------------------------------------------------------------
// asciibuf setup - defines this object and its properties to Pd
//------------------------------------------------------------------------------
void asciibuf_setup( void )
{
// create a new class and assign its pointer to asciibuf_class
asciibuf_class = class_new( gensym( "asciibuf" ), ( t_newmethod )asciibuf_new, ( t_method )asciibuf_free, sizeof( t_asciibuf ), 0, 0 );
// add message handler, responding to any message
class_addmethod( asciibuf_class, ( t_method )asciibuf_message, gensym( "anything" ), A_GIMME, 0 );
// add bang handler
class_addbang( asciibuf_class, ( t_method )asciibuf_bang );
// announce this object in the pd console
Announce( "asciibuf: buffers strings of ascii values - v1.0" );
}
//------------------------------------------------------------------------------
// EOF
//------------------------------------------------------------------------------
// Pd Spectral Toolkit
//
// asciibuf.c
//
// Accepts mixed float / symbol input and sorts it to corresponding outlets
//
// Created by Cooper Baker on 3/29/12.
// Copyright (c) 2012 Cooper Baker. All rights reserved.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// m_pd.h - main header for Pd
//------------------------------------------------------------------------------
#include "m_pd.h"
// utility header for Pd Spectral Toolkit project
#include "utility.h"
//------------------------------------------------------------------------------
// asciibuf_class - pointer to this object's definition
//------------------------------------------------------------------------------
t_class* asciibuf_class;
//------------------------------------------------------------------------------
// asciibuf - data structure holding this object's data
//------------------------------------------------------------------------------
typedef struct asciibuf
{
// this object - must always be first variable in struct
t_object object;
// pointers to the outlets
t_outlet* outlet;
// pointer to float list
t_float* ascii;
// character count
unsigned long chars;
} t_asciibuf;
//------------------------------------------------------------------------------
// function prototypes
//------------------------------------------------------------------------------
void asciibuf_message ( t_asciibuf* object, t_symbol* selector, t_int items, t_atom* list );
void asciibuf_bang ( t_asciibuf* object );
void* asciibuf_new ( void );
void asciibuf_free ( t_asciibuf* object );
void asciibuf_setup ( void );
//------------------------------------------------------------------------------
// asciibuf_bang - shift out a float ( fifo )
//------------------------------------------------------------------------------
void asciibuf_bang( t_asciibuf* object )
{
if( object->chars )
{
// output first ascii value in list
outlet_float( object->outlet, object->ascii[ 0 ] );
// decrement character count
object->chars--;
// shift the ascii buffer
memmove( object->ascii, &( object->ascii[ 1 ] ), object->chars * sizeof( t_float ) );
// trim the ascii buffer
object->ascii = ( t_float* )realloc( object->ascii, object->chars * sizeof( t_float ) );
}
}
//------------------------------------------------------------------------------
// asciibuf_parse - parses list input
//------------------------------------------------------------------------------
void asciibuf_message( t_asciibuf* object, t_symbol* selector, t_int items, t_atom* list )
{
// iterator for input list
t_int i = 0;
// local ascii character count
unsigned long a = 0;
// check for a bad selector
if ( ( StringMatch( selector->s_name, "float" ) == TRUE ) && ( items == 1 ) )
{
; // do nothing for single floats
}
else if( StringMatch( selector->s_name, "list" ) == FALSE )
{
// display an error
pd_error( object, "asciibuf: unknown selector" );
}
// iterate through the input messages
for( i = 0 ; i < items ; i++ )
{
// if the input message is a float
if( list[ i ].a_type == A_FLOAT )
{
// increment the ascii count
a++;
}
// if the input message is a symbol
else if( list[ i ].a_type == A_SYMBOL )
{
// display an error for bad input
pd_error( object, "asciibuf: non-float list items ignored" );
}
}
// allocate more memory for the incoming values
object->ascii = ( t_float* )realloc( object->ascii, ( object->chars + a ) * sizeof( t_float ) );
// reset local ascii character count
a = 0;
// iterate through the input messages
for( i = 0 ; i < items ; i++ )
{
// if the input message is a float
if( list[ i ].a_type == A_FLOAT )
{
// save the float into ascii list
object->ascii[ object->chars + a ] = list[ i ].a_w.w_float;
// increment the ascii count
a++;
}
}
// update character count
object->chars += a;
}
//------------------------------------------------------------------------------
// asciibuf_new - initialize the object upon instantiation ( aka "constructor" )
//------------------------------------------------------------------------------
void* asciibuf_new( void )
{
// declare a pointer to this class
t_asciibuf* object;
// generate a new object and save its pointer in "object"
object = ( t_asciibuf* )pd_new( asciibuf_class );
// generate two new outlets and save their pointers in the object's struct
object->outlet = outlet_new( &object->object, gensym( "float" ) );
// initialize pointer
object->ascii = NULL;
// initialize character count
object->chars = 0;
// return the pointer to this class
return ( void* )object;
}
//------------------------------------------------------------------------------
// teletype_free - cleans up memory allocated by this object
//------------------------------------------------------------------------------
void asciibuf_free( t_asciibuf* object )
{
// clean up the float list
if( object->ascii )
{
free( object->ascii );
object->ascii = NULL;
}
}
//------------------------------------------------------------------------------
// asciibuf setup - defines this object and its properties to Pd
//------------------------------------------------------------------------------
void asciibuf_setup( void )
{
// create a new class and assign its pointer to asciibuf_class
asciibuf_class = class_new( gensym( "asciibuf" ), ( t_newmethod )asciibuf_new, ( t_method )asciibuf_free, sizeof( t_asciibuf ), 0, 0 );
// add message handler, responding to any message
class_addmethod( asciibuf_class, ( t_method )asciibuf_message, gensym( "anything" ), A_GIMME, 0 );
// add bang handler
class_addbang( asciibuf_class, ( t_method )asciibuf_bang );
// announce this object in the pd console
Announce( "asciibuf: buffers strings of ascii values - v1.0" );
}
//------------------------------------------------------------------------------
// EOF
//------------------------------------------------------------------------------